using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using Rhizome.Contract;
using Rhizome.Contract.Diag;
using Rhizome.Internal;
using Rhizome.Model.Validation;

namespace Rhizome.Bindings
{
	internal sealed class ReflectionBindingProxy : BindingProxy
	{
		private PropertyInfo _propertyInfo;

		public ReflectionBindingProxy(string property) : base(property)
		{
		}

		private PropertyInfo PropertyInfo
		{
			[DebuggerStepThrough]
			get { return _propertyInfo ?? (_propertyInfo = GetPropertyInfo()); }
		}

		private PropertyInfo GetPropertyInfo()
		{
			return Plant.GetType().GetProperty(Property);
		}

		#region Implementation of INotifyPropertyChanged

		public override object Value
		{
			[DebuggerStepThrough]
			get { return PropertyInfo.GetValue(Plant, null); }
			[DebuggerStepThrough]
			set { PropertyInfo.SetValue(Plant, value, null); }
		}

		public override Type Type
		{
			[DebuggerStepThrough]
			get { return PropertyInfo.PropertyType; }
		}

		public override IEnumerable<IValidationResult> GetValidationResults()
		{
			if (string.IsNullOrEmpty(Property) == false && PropertyInfo == null)
				yield return ValidationResult.PropertyNotFound(Property, Plant);
		}

		PropertyChangedEventHandler _propertyChanged;

		public override event PropertyChangedEventHandler PropertyChanged
		{
			[DebuggerStepThrough]
			[MethodImpl(MethodImplOptions.Synchronized)] 
			add
			{
				var n = ((INotifyPropertyChanged) Plant);
				if (Property.IsNullOrEmpty())
				{
					n.PropertyChanged += value;
				}
				else
				{
					if (_propertyChanged == null) n.PropertyChanged += OnPropertyChanged;
					_propertyChanged += value;
				}
			}
			[DebuggerStepThrough]
			[MethodImpl(MethodImplOptions.Synchronized)]
			remove
			{
				var n = ((INotifyPropertyChanged)Plant);
				if (Property.IsNullOrEmpty())
				{
					n.PropertyChanged -= value;
				}
				else
				{
					_propertyChanged -= value;
					if (_propertyChanged == null) n.PropertyChanged -= OnPropertyChanged;
				}
			}
		}

		private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			if (e.PropertyName != Property) return;
			var handler = _propertyChanged;
			if (handler != null) handler(sender, e);
		}

		#endregion
	}
}